package com.example.locationtracking.view;

import java.io.IOException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toast;

import com.example.locationtracking.R;
import com.example.locationtracking.adapter.UserSpinnerAdapter;
import com.example.locationtracking.model.User;
import com.example.locationtracking.model.UserLocation;
import com.example.locationtracking.service.LocationUpdateReceiver;
import com.example.locationtracking.service.Service;
import com.example.locationtracking.service.impl.ServiceFactory;
import com.example.locationtracking.utils.Const;
import com.example.locationtracking.utils.LTUtils;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.internal.ad;
import com.google.android.gms.internal.gm;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.CancelableCallback;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapActivity extends BaseActivity implements ConnectionCallbacks,
		OnConnectionFailedListener, OnMarkerClickListener, LocationListener, OnMyLocationButtonClickListener {

	private GoogleMap mMap;
	private LocationClient mLocationClient;
	private Spinner userListSpinner;
	private HashMap<Marker, String> hashMapUser = new HashMap<Marker, String>();
	private String userName="";
	private static final LocationRequest REQUEST = LocationRequest.create()
		      .setInterval(5000)      // 5 seconds
		      .setFastestInterval(3*60*1000) // 3 minutes
		      .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
	private Location currentLocation = null;
	
	protected void initUI() {
		if (!LTUtils.checkInternetConnection(this)) {
			Intent intent = new Intent(this, NetWorkErrorActivity.class);
			intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
			startActivityForResult(intent, Const.NETWORK_ACTIVITY_REQUEST_CODE);
			finish();
			return;
		}
		setContentView(R.layout.map_activity);
		if (!LTUtils.checkLocationService(this))
			showLocationSettingDlg(R.string.msg_location_not_enabled);

		createMapIfReady();
		userName = getIntent().getStringExtra(Const.USER_NAME_KEY) ;
		userListSpinner = (Spinner) findViewById(R.id.user_list);
		setUpLocationClientIfNeeded();
		mLocationClient.connect();
		setUpFindButton();
		loadUserList();
		setController();
	}

	private void loadUserList() {
		Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				Message msg = handler.obtainMessage();
				Service service = ServiceFactory.getInstance().getService();
				try {
					List<User> users = service.getUserList();
					msg.obj = users;

				} catch (Exception e) {
					e.printStackTrace();

				}
				if (msg.obj!=null){
				handler.sendMessage(msg);
				}
			}
		});
		showProgressDlg();
		thread.start();
		
	}

	@SuppressLint("HandlerLeak")
	private Handler handler = new Handler() {
		@SuppressWarnings("unchecked")
		public void handleMessage(Message msg) {
			dismissProgressDlg();
			LinkedList<User> users = new LinkedList<User>((List<User>) msg.obj);
			//remove current user
			/*Iterator<User> iterators = users.iterator();
			while (iterators.hasNext()) {
				User u = iterators.next();
				if(userName.equals(u.getUserName())){
					iterators.remove();
					break;
				}
			}*/

			UserSpinnerAdapter usersAdapter = new UserSpinnerAdapter(
					MapActivity.this, android.R.layout.simple_spinner_item,
					users);
			usersAdapter
					.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
			userListSpinner.setAdapter(usersAdapter);
			selectUser(userName);
		}
	};

	private void setController() {
		userListSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					final int arg2, long arg3) {
				mMap.clear();
				hashMapUser.clear();
				final User user = (User) userListSpinner.getItemAtPosition(arg2);
				if(user.getUserName().equals(userName)){
					navigateToCurrentLocation();
					return;
				}
				// TODO Auto-generated method stub
				Thread thread = new Thread(new Runnable() {

					@Override
					public void run() {
						Message msg = handlerLocation.obtainMessage();
						Service service = ServiceFactory.getInstance()
								.getService();
						
						try {
							UserLocation userLocation = service
									.getLastLocation(user);
							
							msg.obj = userLocation;
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						if (msg.obj!=null){
							handlerLocation.sendMessage(msg);
						}
					}
				});
				showProgressDlg();
				thread.start();
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				System.out.println("nothing selected");
			}
		});
	}

	@SuppressLint("HandlerLeak")
	private Handler handlerLocation = new Handler() {

		public void handleMessage(Message msg) {
			dismissProgressDlg();
			if (msg.obj!=null){
			UserLocation userLocation = (UserLocation) msg.obj;
			LatLng location = new LatLng(Double.parseDouble(userLocation
					.getLatitude()), Double.parseDouble(userLocation
					.getLongitude()));
			changeCamera(location, userLocation.getUsername());
			}else{
				Toast.makeText(MapActivity.this, R.string.msg_noLocation,Toast.LENGTH_LONG).show();
			}
		}
	};

	private void startUpdaterService() {
		Intent alarmIntent = new Intent(this, LocationUpdateReceiver.class);
		PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
				alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
		AlarmManager alarmManager = (AlarmManager) this
				.getSystemService(Context.ALARM_SERVICE);
		alarmManager.setInexactRepeating(AlarmManager.RTC, Calendar
				.getInstance().getTimeInMillis(), Const.UPDATE_INTERVAL * 1000,
				pendingIntent);
	}

	// These settings are the same as the settings for the map. They will in
	// fact give you updates

	private void createMapIfReady() {
		// Do a null check to confirm that we have not already instantiated the
		// map.
		if (mMap == null) {
			// Try to obtain the map from the SupportMapFragment.
			SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
					.findFragmentById(R.id.map);
			mMap = mapFragment.getMap();
			if (mMap != null) {
				mMap.setMyLocationEnabled(true);
				mMap.setOnMarkerClickListener(MapActivity.this);
				mMap.setOnMyLocationButtonClickListener(this);
			}
		}
	}

	private void setUpLocationClientIfNeeded() {
		if (mLocationClient == null) {
			mLocationClient = new LocationClient(getApplicationContext(), this, // ConnectionCallbacks
					this); // OnConnectionFailedListener
		}
	}

	@Override
	public void onPause() {
		super.onPause();
		if (mLocationClient != null) {
			mLocationClient.disconnect();
		}
	}

	@Override
	public void onResume() {
		super.onResume();

		int resultCode = GooglePlayServicesUtil
				.isGooglePlayServicesAvailable(getApplicationContext());

		if (resultCode == ConnectionResult.SUCCESS) {
			/*
			 * Toast.makeText(getApplicationContext(),
			 * "isGooglePlayServicesAvailable SUCCESS",
			 * Toast.LENGTH_LONG).show();
			 */
		} else {
			GooglePlayServicesUtil.getErrorDialog(resultCode, this,
					Const.RQS_GooglePlayServices).show();
		}

	}

	@Override
	public void onConnectionFailed(ConnectionResult result) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onConnected(Bundle connectionHint) {
		mLocationClient.requestLocationUpdates(
                REQUEST,
                (LocationListener) this); 
		startUpdaterService();
	}

	@Override
	public void onDisconnected() {
		mLocationClient.removeLocationUpdates(this);
	}

	/**
	 * Change the camera position by moving or animating the camera depending on
	 * the state of the animate toggle button.
	 */

	private void changeCamera(final LatLng currLng, final String userName) {
		CameraPosition currPos = new CameraPosition.Builder().target(currLng)
				.zoom(15.5f).bearing(0).tilt(25).build();
		mMap.animateCamera(CameraUpdateFactory.newCameraPosition(currPos),
				new CancelableCallback() {
					@Override
					public void onFinish() {
						MarkerOptions mk = new MarkerOptions();
						mk.position(currLng);
						mk.draggable(false);
						mk.title(userName);
						
						Marker m = mMap.addMarker(mk);
						m.showInfoWindow();
						hashMapUser.put(m, userName);
					}

					@Override
					public void onCancel() {
						// Toast.makeText(getBaseContext(),
						// "Animation to Sydney canceled",
						// Toast.LENGTH_SHORT).show();
					}
				});

	}

	private void setUpFindButton() {
		// Getting reference to btn_find of the layout activity_main
		Button btn_find = (Button) findViewById(R.id.btn_find);

		// Defining button click event listener for the find button
		View.OnClickListener findClickListener = new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// Getting reference to EditText to get the user input location
				EditText etLocation = (EditText) findViewById(R.id.et_location);

				// Getting user input location
				String location = etLocation.getText().toString();

				if (location != null && !location.equals("")) {
					new GeocoderTask().execute(location);
				}
			}
		};

		// Setting button click event listener for the find button
		btn_find.setOnClickListener(findClickListener);
	}

	// An AsyncTask class for accessing the GeoCoding Web Service
	private class GeocoderTask extends AsyncTask<String, Void, List<Address>> {

		protected List<Address> doInBackground(String... locationName) {
			// Creating an instance of Geocoder class
			Geocoder geocoder = new Geocoder(getBaseContext());
			List<Address> addresses = null;

			try {
				// Getting a maximum of 3 Address that matches the input text
				addresses = geocoder.getFromLocationName(locationName[0], 3);
			} catch (IOException e) {
				e.printStackTrace();
			}
			return addresses;
		}

		protected void onPostExecute(List<Address> addresses) {

			if (addresses == null || addresses.size() == 0) {
				Toast.makeText(getBaseContext(), "No Location found",
						Toast.LENGTH_SHORT).show();
			}

			// Clears all the existing markers on the map
			mMap.clear();

			// Adding Markers on Google Map for each matching address
			for (int i = 0; i < addresses.size(); i++) {

				Address address = (Address) addresses.get(i);

				// Creating an instance of GeoPoint, to display in Google Map
				LatLng latLng = new LatLng(address.getLatitude(),
						address.getLongitude());

				String addressText = String.format(
						"%s, %s",
						address.getMaxAddressLineIndex() > 0 ? address
								.getAddressLine(0) : "", address
								.getCountryName());

				MarkerOptions markerOptions = new MarkerOptions();
				markerOptions.position(latLng);
				markerOptions.title(addressText);

				mMap.addMarker(markerOptions);

				// Locate the first location
				if (i == 0)
					mMap.animateCamera(CameraUpdateFactory.newLatLng(latLng));
			}
		}
	}

	@Override
	public boolean onMarkerClick(Marker arg0) {
		// TODO Auto-generated method stub
		String userName = hashMapUser.get(arg0);
		if (userName != null) {
			Intent intent = new Intent(getApplicationContext(),
					LocationHistoryActivity.class);
			intent.putExtra(Const.USER_NAME_KEY, userName);
			startActivity(intent);
			//MapActivity.this.finish();
		} else {
			return false;
		}
		return false;
	}

	@Override
	public void onLocationChanged(Location arg0) {
		// TODO Auto-generated method stub
		currentLocation = arg0;
		if(((User)userListSpinner.getSelectedItem()).getUserName().equals(userName))
			navigateToCurrentLocation();
		//mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
		LTUtils.updateLocation(this, arg0);
	}
	private void selectUser(String userName){
		SpinnerAdapter adapter = userListSpinner.getAdapter();
		for(int i = 0;i< adapter.getCount();i++){
			User u = (User) adapter.getItem(i);
			if(u.getUserName().equals(userName)){
				userListSpinner.setSelection(i);
				return;
			}
		}
	}
	@Override
	public boolean onMyLocationButtonClick() {
		if(((User)userListSpinner.getSelectedItem()).getUserName().equals(userName))
			return navigateToCurrentLocation();
		else{
			selectUser(userName);
		}
		return false;
	}
	private boolean navigateToCurrentLocation(){
		mMap.clear();
		hashMapUser.clear();
		
		Location location = currentLocation;
		if(location==null)
			location = mLocationClient.getLastLocation();
		if(location==null)
			return false;
		final LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
		
		// Showing the current location in Google Map
		//mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
		
		// Zoom in the Google Map
		
		CameraPosition currPos = new CameraPosition.Builder().target(latLng)
				.zoom(15.5f).bearing(0).tilt(25).build();
		mMap.animateCamera(CameraUpdateFactory.newCameraPosition(currPos),
				new CancelableCallback() {
					@Override
					public void onFinish() {
						MarkerOptions mk = new MarkerOptions();
						mk.position(latLng);
						mk.draggable(false);
						mk.title("Me");
						Marker m = mMap.addMarker(mk);
						m.showInfoWindow();
						hashMapUser.put(m, userName);
						
					}

					@Override
					public void onCancel() {
						
					}
				});
		return true;
	}
}
